/*
 * Softmax.cpp
 *
 *  Created on: 2018��7��31��
 *      Author: wangzhendong
 */

#include <Softmax.h>
#include <DenseVec.h>
#include<Datas.h>


int Softmax::m;

Vec* Softmax::gra(const Vec &current, Datas* datas, double lamada) {
	Vec* result = current * lamada;
	for (int kk = 0; kk < (m - 1); kk++) {
		for (int j = 0; j < datas->getLen(); j++) {
			double expsum = 0.0;
			double expk = 0.0;
			Vec* x = datas->getData(j).x;
			double y = datas->getData(j).y;
			for (int k = 0; k < (m - 1); k++) {
				double sum = 0.0;
				for (int n = 0; n < datas->getDim(); n++) {
					sum += current.get(k * datas->getDim() + n) * x->get(n);
				}
				expsum += log(sum);
				if (((int) kk) == k) {
					expk = log(sum);
				}
			}
			double scale = expk / (1.0 + expsum);
			for (int n = 0; n < datas->getDim(); n++) {
				int index = kk * datas->getDim() + n;
				result->set(index, result->get(index) + x->get(n) * scale);
				if (kk == ((int) y)) {
					result->set(index, result->get(index) - x->get(n));
				}
			}

		}
	}
	return result;
}

double Softmax::func(const Vec& current, Datas* datas, double lamada) {
	double result = 0.0;
	for (int i = 0; i < current.len(); i++) {
		result += pow(current.get(i), 2);
	}
	result *= lamada;
	result /= 2.0;
	for (int j = 0; j < datas->getLen(); j++) {
		double expsum = 0.0;
		Vec* x = datas->getData(j).x;
		double y = datas->getData(j).y;
		for (int k = 0; k < (m - 1); k++) {
			double sum = 0.0;
			for (int n = 0; n < x->len(); n++) {
				sum += current.get(k * x->len() + n) * x->get(n);
			}
			expsum += exp(sum);
		}
		expsum += 1.0;
		expsum = log(expsum);
		if (y < (m - 1)) {
			for (int n = 0; n < x->len(); n++) {
				expsum -= current.get(((int) y) * x->len() + n) * x->get(n);
			}
		}
	}
	return result;
}

Vec* Softmax::learn(Datas* ds,double lamada){
	Vec * init = new DenseVec(ds->getDim()*m);
	for(int i=0;i<init->len();i++){
		init->set(i,1.0);
	}
	framework->getGra()->setDatas(ds);
	framework->getGra()->setLamada(lamada);
	framework->getFunc()->setDatas(ds);
	framework->getFunc()->setLamada(lamada);
	Vec* result = framework->process(*init);
	delete init;
	return result;
}

